using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Data;
using System.Web;
using System.Collections.Specialized;
using System.Web.UI.WebControls;
using System.Text.RegularExpressions;
using System.Diagnostics;
using System.Web.UI;
using System.Xml;
using Microsoft.SharePoint.Utilities;
using System.ComponentModel;
using System.Security.Permissions;
using Microsoft.SharePoint.Security;

namespace ARF.Web.Controls
{
    [ToolboxData("<{0}:SiteQuery runat=server></{0}:SiteQuery>"), ToolboxItemFilter("ARF")]
    public class SiteQuery : XmlProvider
    {
        private string _lists = "";
        private string _query = "";
        private string _webs = "<Webs Scope='Recursive' />";
        private string _viewFields = "<FieldRef Name='Title' /><FieldRef Name='ID' /><FieldRef Name='FileRef' /><FieldRef Name='ContentType' /><FieldRef Name='ContentTypeId' />";
        private string _additionalViewFields = "";

        private int _rowLimit = 10;

        private string _web = "";
        private string _serverTemplate = "";

        private bool _replaceQStringTokens = true;
        private bool _replaceFormTokens = false;
        private bool _replaceFieldTokens = false;
        private bool _replaceDates = false;

        private bool _fixLookupNames = false;

        public bool FixLookupNames
        {
            get { return _fixLookupNames; }
            set { _fixLookupNames = value; }
        }

        public bool ReplaceDates
        {
            get { return _replaceDates; }
            set { _replaceDates = value; }
        }

        private string _queryFilter;

        public string QueryFilter
        {
            get { return _queryFilter; }
            set { _queryFilter = value; }
        }

        public virtual string AdditionalViewFields
        {
            get { return _additionalViewFields; }
            set { _additionalViewFields = value; }
        }

        public bool ReplaceFieldTokens
        {
            get { return _replaceFieldTokens; }
            set { _replaceFieldTokens = value; }
        }

        public bool ReplaceFormTokens
        {
            get { return _replaceFormTokens; }
            set { _replaceFormTokens = value; }
        }

        public bool ReplaceQStringTokens
        {
            get { return _replaceQStringTokens; }
            set { _replaceQStringTokens = value; }
        }

        public string ServerTemplate
        {
            get { return _serverTemplate; }
            set { _serverTemplate = value; }
        }

        private SPWeb _webToDispose = null;

        public string WebUrl
        {
            get { return _web; }
            set { _web = value; }
        }

        #region "SiteDataQuery Properties"

        public virtual string Lists
        {
            get 
            {
                if (!string.IsNullOrEmpty(_lists)) return _lists;

                if (!string.IsNullOrEmpty(ServerTemplate)) return string.Format("<Lists ServerTemplate='{0}'/>", ServerTemplate);

                return _lists; 
            }
            set { _lists = value; }
        }

        public virtual string Query
        {
            get { return _query; }
            set { _query = value; }
        }

        public virtual string Webs
        {
            get { return _webs; }
            set { _webs = value; }
        }

        public virtual string ViewFields
        {
            get { return _viewFields; }
            set { _viewFields = value; }
        }

        public virtual int RowLimit
        {
            get { return _rowLimit; }
            set { _rowLimit = value; }
        }
        #endregion

        protected override void BuildXml()
        {
            try
            {
                SPWeb oWeb = GetWebForQuery();

                DataTable oTable = ExecuteDataQuery(oWeb);
                oTable.TableName = "row";
                DataSet oDataset = new DataSet("rows");
                oDataset.Tables.Add(oTable);
                XmlAsString = oDataset.GetXml();
            }
            catch (Exception ex)
            {
                Log.Trace(ex);
            }
        }

        public override void Dispose()
        {
            if (_webToDispose != null) _webToDispose.Dispose();

            base.Dispose();
        }

        protected virtual SPWeb GetWebForQuery()
        {
            if (string.IsNullOrEmpty(WebUrl))
                return SPContext.Current.Web;

            if (WebUrl == "/") return SPContext.Current.Site.RootWeb;

            if (WebUrl.StartsWith("/"))
            {
                _webToDispose = SPContext.Current.Site.OpenWeb(WebUrl);
                return _webToDispose;
            }
            else
            {
                string sUrl = SPContext.Current.Web.Url + WebUrl;
                _webToDispose = SPContext.Current.Site.OpenWeb(sUrl);
                return _webToDispose;
            }
        }

        protected virtual string ConfigureQuery(string queryXML)
        {
            if (!string.IsNullOrEmpty(QueryFilter)) return GetQueryFromFilter(queryXML);

            StringBuilder sb = new StringBuilder(queryXML);

            if (ReplaceQStringTokens) ReplaceTokens(queryXML, "**", sb, QueryString);
            if (ReplaceFormTokens) ReplaceTokens(queryXML, "~~", sb, HttpContext.Current.Request.Form);
            if (ReplaceFieldTokens) ReplaceColumnTokens(queryXML, sb);

            sb.Replace("[Today]", SPUtility.CreateISO8601DateTimeFromSystemDateTime(new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day)));
            sb.Replace("[Now]", SPUtility.CreateISO8601DateTimeFromSystemDateTime(DateTime.Now));
            sb.Replace("[Midnight]", SPUtility.CreateISO8601DateTimeFromSystemDateTime(new DateTime(DateTime.Now.AddDays(1).Year, DateTime.Now.AddDays(1).Month, DateTime.Now.AddDays(1).Day, 0,0,0)));

            if (ReplaceDates)
            {
                DateTime dtCurrent = DateTime.Now.AddDays(-7);
                for (int c = -7; c < 8; c++)
                {
                    string sSign = ((c < 0) ? "-" : "+");
                    if (c != 0)
                        sb.Replace("[Today]" + sSign + c.ToString(), SPUtility.CreateISO8601DateTimeFromSystemDateTime(dtCurrent));

                    dtCurrent = dtCurrent.AddDays(1);
                }
            }

            return FixMultiLookupNames(sb);
        }

        private string FixMultiLookupNames(StringBuilder sb)
        {
            if (!FixLookupNames) return sb.ToString();

            XmlDocument oDoc = new XmlDocument();
            oDoc.LoadXml("<xml>" + sb.ToString() + "</xml>");
            XmlNodeList oList = oDoc.SelectNodes("//FieldRef");
            foreach (XmlElement oField in oList)
            {
                try
                {
                    string sFieldName = oField.GetAttribute("Name");
                    SPField f = SPContext.Current.Site.RootWeb.Fields.GetFieldByInternalName(sFieldName);
                    if (f.TypeAsString == "LookupMulti")
                    {
                        oField.RemoveAttribute("Name");
                        oField.SetAttribute("ID", f.Id.ToString().Replace("{", "").Replace("}", ""));
                    }
                }
                catch (Exception ex)
                {
                    Log.Trace(ex);
                }
            }
            return oDoc.DocumentElement.InnerXml;
        }
        
        protected virtual string GetQueryFromFilter(string queryXML)
        {
            Control c = FindControlOnPage(QueryFilter);
            if (c is QueryFilter) return ((QueryFilter)c).BuildQuery(queryXML);

            Trace.WriteLine("QueryFilter not found: " + QueryFilter);

            return queryXML;
        }

        private Control FindControlOnPage(string QueryFilter)
        {
            return Utility.Utility.FindControlOnPage(this, QueryFilter);
        }

        private static void ReplaceColumnTokens(string queryXML, StringBuilder sb)
        {
            Regex rg = new Regex("@@(.*?)@@");
            MatchCollection mc = rg.Matches(queryXML);
            SPListItem oCurrent = SPContext.Current.ListItem;

            foreach (Match m in mc)
            {
                try
                {
                    object o = oCurrent[m.Value];
                    if (o != null)
                    {
                        sb.Replace("@@" + m.Value + "@@", o.ToString());
                    }
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(ex);
                }
            }
        }

        private void ReplaceTokens(string queryXML, string sTokenDelimiter, StringBuilder sb, NameValueCollection collection)
        {
            if (queryXML.Contains(sTokenDelimiter))
            {
                foreach (string sKey in collection.AllKeys)
                {
                    string sLowerKey = sKey.ToLower();
                    sb.Replace(sTokenDelimiter + sLowerKey + sTokenDelimiter, collection[sLowerKey]);
                }
            }
        }

        protected virtual DataTable ExecuteDataQuery(SPWeb oWeb)
        {
            SPSiteDataQuery query = new SPSiteDataQuery();
            query.Lists = Lists; 
            query.Query = ConfigureQuery(Query);
            query.Webs = Webs;
            query.ViewFields = FixMultiLookupNames(new StringBuilder(ViewFields + AdditionalViewFields));
            query.RowLimit = (uint)RowLimit;
            return oWeb.GetSiteData(query);
        }

    }
}
